package com.kinyx.framework.business;

import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;

import com.kinyx.framework.annotation.assist.CanNotNull;
import com.kinyx.framework.annotation.assist.Note;
import com.kinyx.framework.annotation.assist.ReturnNotNull;
import com.kinyx.framework.annotation.assist.Tester;
import com.kinyx.framework.annotation.assist.Throws;
import com.kinyx.framework.enums.impl.ErrorCode0;
import com.kinyx.framework.exception.KRuntimeException;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

/**
 * HEX工具类
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class KHexUtils {

	private static final Map<Character, Integer> MAPPING = new HashMap<>();
	static {
		for (char c = '0'; c <= '9'; c++) { MAPPING.put(c, c - '0'); }
		for (char c = 'A'; c <= 'F'; c++) { MAPPING.put(c, (c - 'A') + 10); }
		for (char c = 'a'; c <= 'f'; c++) { MAPPING.put(c, (c - 'a') + 10); }
	}

	/** 十六进制字符数组 */
	private static final char[] HEX_CHARS = {
			'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
	};
	/** 十六进制 */
	private static final int HEX = 16;

	/**
	 * 字节值转字符值（高位）
	 * @param b 字节值
	 * @return 字符值
	 */
	@ReturnNotNull
	@Tester("com.kinyx.framework.business.KHexUtils_Tester_byte2charH")
	public static char byte2charH(@CanNotNull final byte b) { return HEX_CHARS[(b >>> 0x4) & 0xf]; }

	/**
	 * 字节值转字符值（低位）
	 * @param b 字节值
	 * @return 字符值
	 */
	@ReturnNotNull
	@Tester("com.kinyx.framework.business.KHexUtils_Tester_byte2charL")
	public static char byte2charL(@CanNotNull final byte b) { return HEX_CHARS[b & 0xf]; }

	/**
	 * 字节值转字符数组
	 * @param b 字节值
	 * @return 字符数组
	 */
	@ReturnNotNull
	public static char[] byte2chars(@CanNotNull final byte b) {
		return new char[]{
				byte2charH(b), byte2charL(b)
		};
	}

	@Throws(at = "KHexUtils.check(int)", value = "ErrorCode0.ValueLessThenMinValue", when = "i < 0")
	@Throws(at = "KHexUtils.check(int)", value = "ErrorCode0.ValueMoreThenMaxValue", when = "i > 255")
	private static void check(@CanNotNull final int i) {
		if (i < 0) {
			throw new KRuntimeException(ErrorCode0.ValueLessThenMinValue).param("value", i).param("minValue", 0);
		}
		if (i > 255) {
			throw new KRuntimeException(ErrorCode0.ValueMoreThenMaxValue).param("value", i).param("maxValue", 255);
		}
	}

	/**
	 * 整型值转字符值（高位）
	 * @param i 整型值
	 * @return 字符值
	 */
	@ReturnNotNull
	@Throws(at = "KHexUtils.check(int)", value = "ErrorCode0.ValueLessThenMinValue", when = "i < 0")
	@Throws(at = "KHexUtils.check(int)", value = "ErrorCode0.ValueMoreThenMaxValue", when = "i > 255")
	public static char int2charH(@CanNotNull final int i) { check(i); return HEX_CHARS[(i >>> 0x4) & 0xf]; }

	/**
	 * 整型值转字符值（低位）
	 * @param i 整型值
	 * @return 字符值
	 */
	@ReturnNotNull
	@Throws(at = "KHexUtils.check(int)", value = "ErrorCode0.ValueLessThenMinValue", when = "i < 0")
	@Throws(at = "KHexUtils.check(int)", value = "ErrorCode0.ValueMoreThenMaxValue", when = "i > 255")
	public static char int2charL(@CanNotNull final int i) { check(i); return HEX_CHARS[i & 0xf]; }

	/**
	 * 整型值转字符值数组
	 * @param i 整型值
	 * @return 字符数组
	 */
	@ReturnNotNull
	@Throws(at = "KHexUtils.check(int)", value = "ErrorCode0.ValueLessThenMinValue", when = "i < 0")
	@Throws(at = "KHexUtils.check(int)", value = "ErrorCode0.ValueMoreThenMaxValue", when = "i > 255")
	public static char[] int2chars(@CanNotNull final int i) {
		return new char[]{
				int2charH(i), int2charL(i)
		};
	}

	/**
	 * 字符值转整型值
	 * @param c 字符值
	 * @return 整型值
	 */
	@ReturnNotNull
	@Tester("com.kinyx.framework.business.KHexUtils_Tester_char2int")
	public static int char2int(@CanNotNull final char c) {
		final Integer i = MAPPING.get(c);
		if (i == null) { throw new IllegalArgumentException(); }
		return i;
	}

	/**
	 * 字符值转字节值
	 * @param c 字符值
	 * @return 字节值
	 */
	@ReturnNotNull
	@Tester("com.kinyx.framework.business.KHexUtils_Tester_char2byte")
	public static byte char2byte(@CanNotNull final char c) { return (byte) char2int(c); }

	// ===================================================== hex2xxx ===================================================== //
	/**
	 * 十六进制字符串转字节数组
	 * @param hex 十六进制字符串
	 * @return 字节数组
	 */
	@ReturnNotNull
	@Tester("com.kinyx.framework.business.KHexUtils_Tester_hex2bytes_0_bytes2hex")
	public static byte[] hex2bytes(@CanNotNull final String hex) {
		final int length = hex.length();
		if ((length % 2) != 0) { return hex2bytes("0" + hex); }
		final byte[] bytes = new byte[length / 2];
		for (int i = 0, j = 0; i < bytes.length; i++, j = i * 2) {
			bytes[i] = (byte) ((HEX * char2byte(hex.charAt(j))) + char2byte(hex.charAt(j + 1)));
		}
		return bytes;
	}

	/**
	 * 十六进制字符串转整型数组
	 * @param hex 十六进制字符串
	 * @return 整型数组
	 */
	@ReturnNotNull
	@Tester("com.kinyx.framework.business.KHexUtils_Tester_hex2ints_0_ints2hex")
	public static int[] hex2ints(@CanNotNull final String hex) {
		final int length = hex.length();
		if ((length % 2) != 0) { return hex2ints("0" + hex); }
		final int[] ints = new int[length / 2];
		for (int i = 0, j = 0; i < ints.length; i++, j = i * 2) {
			ints[i] = (HEX * char2int(hex.charAt(j))) + char2int(hex.charAt(j + 1));
		}
		return ints;
	}

	@ReturnNotNull
	@Throws(at = "KHexUtils.checkMax(String, BigInteger)", value = "ErrorCode0.ValueMoreThenMaxValue", when = "值大于最大值")
	private static BigInteger checkMax(@CanNotNull final String hex, @CanNotNull final BigInteger max) {
		final BigInteger bi = hex2bigInt(hex);
		if (bi.compareTo(max) > 0) {
			throw new KRuntimeException(ErrorCode0.ValueMoreThenMaxValue).param("value", bi.toString()).param("maxValue", max.toString());
		}
		return bi;
	}

	/**
	 * 十六进制字符串转字节值
	 * @param hex 十六进制字符串
	 * @return 字节值
	 */
	private static BigInteger MAX_BYTE = BigInteger.valueOf(255);
	@ReturnNotNull
	@Throws(at = "KHexUtils.checkMax(String, BigInteger)", value = "ErrorCode0.ValueMoreThenMaxValue", when = "值大于最大值")
	@Tester("com.kinyx.framework.business.KHexUtils_Tester_hex2byte_0_byte2hex")
	public static byte hex2byte(@CanNotNull final String hex) { return (byte) checkMax(hex, MAX_BYTE).intValue(); }

	/**
	 * 十六进制字符串转整型值
	 * @param hex 十六进制字符串
	 * @return 整型值
	 */
	private static BigInteger MAX_INT = BigInteger.valueOf(Integer.MAX_VALUE);
	@ReturnNotNull
	@Throws(at = "KHexUtils.checkMax(String, BigInteger)", value = "ErrorCode0.ValueMoreThenMaxValue", when = "值大于最大值")
	@Tester("com.kinyx.framework.business.KHexUtils_Tester_hex2int_0_int2hex")
	public static int hex2int(@CanNotNull final String hex) { return checkMax(hex, MAX_INT).intValue(); }

	/**
	 * 十六进制字符串转长整型值
	 * @param hex 十六进制字符串
	 * @return 长整型值
	 */
	private static BigInteger MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE);
	@ReturnNotNull
	@Throws(at = "KHexUtils.checkMax(String, BigInteger)", value = "ErrorCode0.ValueMoreThenMaxValue", when = "值大于最大值")
	@Tester("com.kinyx.framework.business.KHexUtils_Tester_hex2long_0_long2hex")
	public static long hex2long(@CanNotNull final String hex) { return checkMax(hex, MAX_LONG).longValue(); }

	/**
	 * 十六进制字符串转大整型值
	 * @param hex 十六进制字符串
	 * @return 大整型值
	 */
	@ReturnNotNull
	@Tester("com.kinyx.framework.business.KHexUtils_Tester_hex2bigInt_0_bigInt2hex")
	public static BigInteger hex2bigInt(@CanNotNull final String hex) {
		final int length = hex.length();
		if ((length % 2) != 0) { return hex2bigInt("0" + hex); }
		return new BigInteger(hex, 16);
	}

	// ===================================================== xxx2hex ===================================================== //
	/**
	 * 字节数组转十六进制字符串
	 * @param bytes 字节数组
	 * @return 十六进制字符串
	 */
	@ReturnNotNull
	@Note("小写")
	@Tester("com.kinyx.framework.business.KHexUtils_Tester_hex2bytes_0_bytes2hex")
	public static String bytes2hex(@CanNotNull final byte[] bytes) {
		final char[] chars = new char[bytes.length * 2];
		for (int i = 0; i < bytes.length; i++) {
			final byte b = bytes[i];
			final int j = i << 1;
			chars[j] = byte2charH(b);
			chars[j + 1] = byte2charL(b);
		}
		return new String(chars);
	}

	/**
	 * 整型数组转十六进制字符串
	 * @param ints 整型数组
	 * @return 十六进制字符串
	 */
	@ReturnNotNull
	@Note("小写")
	@Throws(at = "KHexUtils.check(int)", value = "ErrorCode0.ValueLessThenMinValue", when = "i < 0")
	@Throws(at = "KHexUtils.check(int)", value = "ErrorCode0.ValueMoreThenMaxValue", when = "i > 255")
	@Tester("com.kinyx.framework.business.KHexUtils_Tester_hex2ints_0_ints2hex")
	public static String ints2hex(@CanNotNull final int[] ints) {
		final char[] chars = new char[ints.length * 2];
		for (int i = 0; i < ints.length; i++) {
			final int x = ints[i];
			check(x);
			final int j = i << 1;
			chars[j] = int2charH(x);
			chars[j + 1] = int2charL(x);
		}
		return new String(chars);
	}

	/**
	 * 字节值转十六进制字符串
	 * @param b 字节值
	 * @return 十六进制字符串
	 */
	@ReturnNotNull
	@Note("小写")
	@Tester("com.kinyx.framework.business.KHexUtils_Tester_hex2byte_0_byte2hex")
	public static String byte2hex(@CanNotNull final byte b) {
		return new String(byte2chars(b));
	}

	/**
	 * 整型值值转十六进制字符串
	 * @param i 整型值
	 * @return 十六进制字符串
	 */
	@ReturnNotNull
	@Note("小写")
	@Tester("com.kinyx.framework.business.KHexUtils_Tester_hex2int_0_int2hex")
	public static String int2hex(@CanNotNull final int i) {
		final String hex = Integer.toHexString(i);
		return ((hex.length() % 2) == 1 ? "0" : "") + hex;
	}

	/**
	 * 长整型值转十六进制字符串
	 * @param l 长整型值
	 * @return 十六进制字符串
	 */
	@ReturnNotNull
	@Note("小写")
	@Tester("com.kinyx.framework.business.KHexUtils_Tester_hex2long_0_long2hex")
	public static String long2hex(@CanNotNull final long l) {
		final String hex = Long.toHexString(l);
		return ((hex.length() % 2) == 1 ? "0" : "") + hex;
	}

	/**
	 * 大整型值转十六进制字符串
	 * @param bi 大整型值
	 * @return 十六进制字符串
	 */
	@ReturnNotNull
	@Note("小写")
	@Tester("com.kinyx.framework.business.KHexUtils_Tester_hex2bigInt_0_bigInt2hex")
	public static String bigInt2hex(@CanNotNull final BigInteger bi) {
		final String hex = bi.toString(HEX);
		return ((hex.length() % 2) == 1 ? "0" : "") + hex;
	}

}
